﻿using AppFramework.Logger;
using APS_Define_W32;
using APS168_W64;
using PerpheralDevice.Units;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace PerpheralDevice.AdLink
{
    public class AdLinkMotor : Motor,IDisposable {
        private readonly ILogger _logger = ILogger.Default;

        #region 伺服应用操作

        public override Motor Reset() {
            this._logger.Info($"{this.GetType().FullName}:{this.PerpDev.PerpDevName}=>{nameof(Reset)}({this.UnitID})");

            if (this.PerpDev.IsMocking) {
                this.MotorState = MotorState.Waiting;
                return this;
            }

            if (this.MotorState == MotorState.Unkown) {
                //回零过程.01=>高速
                APS168.APS_set_axis_param_f(this.mfAxisNO, (Int32)APS_Define.PRA_HOME_POS, 0);
                Thread.Sleep(500);
                int tempRetCode01 = APS168.APS_home_move(this.mfAxisNO);
                if (tempRetCode01 != (Int32)APS_Define.ERR_NoError) {
                    // 以第一次回零10%的速度再做一次回零
                    int tempHomeVel = 0;
                    APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_VM, ref tempHomeVel);
                    APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_VM, tempHomeVel / 10);
                    int tempRetCode02 = APS168.APS_home_move(this.mfAxisNO);
                    APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_VM, tempHomeVel);
                    if (tempRetCode02 != (Int32)APS_Define.ERR_NoError) {
                        this.MotorState = MotorState.Waiting;
                    }
                }                
            }

            while (this.IsMoving) {
                System.Threading.Thread.Sleep(50);
            }

            return this;
        }

        public override Motor ReachRel(int posDst) {
            if (this.PerpDev.IsMocking) return this;
            this._logger?.Debug($"运动轴({this.mfCardNO},{this.mfAxisNO},指令脉冲位置: {this.PosCommand},编码器位置: {this.PosEncoder})前往({posDst})开始...");
            
            // 定长运动
            var xx= APS168.APS_relative_move(this.mfAxisNO, posDst, this.VelWork);
            //while (this.IsMoving) {
            //    Task.Delay(50).Wait();
            //}
            this._logger?.Debug($"运动轴({this.mfCardNO},{this.mfAxisNO},指令脉冲位置: {this.PosCommand},编码器位置: {this.PosEncoder})前往({posDst})结束");

            return this;
        }

        public override Motor ReachAbs(int posDst) {
            if (this.PerpDev.IsMocking) return this;
            this._logger?.Debug($"运动轴({this.mfCardNO},{this.mfAxisNO},指令脉冲位置: {this.PosCommand},编码器位置: {this.PosEncoder})前往({posDst})开始...");
            APS168.APS_absolute_move(this.mfAxisNO, posDst, this.VelWork);
            while (this.IsMoving) {
                Task.Delay(50).Wait();
            }
            this._logger?.Debug($"运动轴({this.mfCardNO},{this.mfAxisNO},指令脉冲位置: {this.PosCommand},编码器位置: {this.PosEncoder})前往({posDst})结束");
            
            return this;
        }

        public override Motor DoMoving() {
            this._logger.Info($"{this.GetType().FullName}:{this.PerpDev.PerpDevName}=>{nameof(DoMoving)}({this.UnitID})");
            if (this.PerpDev.IsMocking) return this;

            // 连续运动
            //APS168.APS_velocity_move(this.mfAxisNO, this.VelWork);
            APS168.APS_jog_start(this.mfAxisNO, 1);
            return this;
        }

        public override Motor Stop() {
            this._logger.Info($"{this.GetType().FullName}:{this.PerpDev.PerpDevName}=>{nameof(Stop)}({this.UnitID})");
            do {
                APS168.APS_jog_start(this.mfAxisNO, 0);
                APS168.APS_stop_move(this.mfAxisNO);
            } while (this.IsMoving);
            this.OnStatusChangedRaise();
            return this;
        }

        public override Motor Scram() {
            this.MotorState =  MotorState.Unkown;
            do {
                APS168.APS_emg_stop(this.mfAxisNO);
            } while (this.IsMoving);

            return this;
        }

        public override Motor ServoEnable()
        {
            APS168.APS_set_servo_on(this.mfAxisNO, 1);
            return this;
        }

        public override Motor ServoDisable()
        {
            APS168.APS_set_servo_on(this.mfAxisNO, 0);
            return this;
        }

        #endregion

        #region 伺服速度属性 (PCI-8254/58,AMP-204/8C 轴参数表: 回零、点动、运行的速度，加速度等仅有一个接口, 基于伺服只会在一个模式下运动)

        private int mfVelBoot;  //启动速度
        private int mfVelWork;  //最大速度
        private int mfVelStop;  //停止速度
        private int mfVelAcc;   //加速率
        private int mfVelDec;   //减速率
        private AdLinkMotor getVel() {
            APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VS, ref this.mfVelBoot);
            APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VM, ref this.mfVelWork);
            APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VE, ref this.mfVelStop);

            APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_ACC, ref this.mfVelAcc);
            APS168.APS_get_axis_param(this.mfAxisNO, (int)APS_Define.PRA_DEC, ref this.mfVelDec);

            return this;
        }

        public override int VelBoot => (int)this.getVel().mfVelBoot;
        public override Motor VelBootSet(int velBoot) {
            if (this.VelBoot != velBoot) {
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_VS, velBoot);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VS, velBoot);
            }
            return this;
        }

        public override int VelWork => (int)this.getVel().mfVelWork;
        public override Motor VelWorkSet(int velWork) {
            if (this.VelWork != velWork) {
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_VM, velWork);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VM, velWork);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_JG_VM, velWork);                
            }
            APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_JG_DIR, velWork > 0 ? 0 : 1); //点动方向 0: 正方向 1: 反方向
            return this;
        }

        public override int VelStop => (int)this.getVel().mfVelStop;
        public override Motor VelStopSet(int velStop) {
            if (this.VelStop != velStop) {
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_VE, velStop);
            }
            return this;
        }

        public override int VelAcc => this.getVel().mfVelAcc;
        public override Motor VelAccSet(int velAcc) {
            if (this.VelAcc != velAcc) {
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_HOME_ACC, velAcc);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_ACC, velAcc);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_JG_ACC, velAcc);
            }
            return this;
        }

        public override int VelDec => this.getVel().mfVelDec;
        public override Motor VelDecSet(int velDec) {
            if (this.VelDec != velDec) {
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_DEC, velDec);
                APS168.APS_set_axis_param(this.mfAxisNO, (int)APS_Define.PRA_JG_DEC, velDec);
            }
            return this;
        }

        public override double TimeDec => default;
        public override Motor TimeDecSet(double timeDec) {
            return this;
        }

        public override double TimeAcc => default;
        public override Motor TimeAccSet(double timeAcc) {
            return this;
        }

        private MotorStatus mfMotorStatus;
        /// <summary>
        /// 运动状态/状态IO
        /// </summary>
        public override MotorStatus MotorStatus {
            get {
                int tempBitStatus = APS168.APS_motion_status(this.mfAxisNO);
                int tempBitStatusIO = APS168.APS_motion_io_status(this.mfAxisNO);
                return new MotorStatus(tempBitStatusIO, tempBitStatus);
            }
        }

        public override bool DirMoveIsForward => throw new NotImplementedException();
        public override Motor DirMoveIsForwardSet(bool isForward) {
            return this;
        }

        #endregion

        private readonly ushort mfCardNO;
        private readonly ushort mfAxisNO;
        /// <summary>
        /// 运动卡ID
        /// </summary>
        public object BoardID { get; }
        /// <summary>
        /// 运动轴序号
        /// </summary>
        public object AxisID { get; }

        /// <summary>
        /// 指令脉冲位置
        /// </summary>
        public override int PosCommand {
            get {
                double tempPosition = default;
                APS168.APS_get_command_f(this.mfAxisNO, ref  tempPosition);
                return (int)tempPosition;
            }
        }
        /// <summary>
        /// 编码器位置
        /// </summary>
        public override int PosEncoder {
            get {
                double tempPosition = default;
                APS168.APS_get_position_f(this.mfAxisNO, ref tempPosition);
                return (int)tempPosition;
            }
        }

        public override string UnitID { get; }

        public override bool IsMoving => !this.MotorStatus.MTS_MDN;

        public override bool IsMovedOK => this.MotorStatus.MIO_INP;

        

        protected bool mfIsPolling = true;
        protected Task mfPollingTask = null;
        /// <summary>
        /// 运动轴 构造
        /// </summary>
        /// <param name="motionCard">运动卡</param>
        /// <param name="axisNO">运动轴序号</param>
        public AdLinkMotor(PerpDev perpDev, int axisNO) {
            this.PerpDev = perpDev;
            this.mfAxisNO = (ushort)axisNO;
            this.UnitID = (axisNO+1).ToString();
            if (!this.PerpDev.IsMocking) {
                this.ServoEnable();
                this.mfPollingTask = Task.Factory.StartNew(() => {
                    while (this.mfIsPolling) {
                        if (!this.mfMotorStatus.Equals(this.MotorStatus)) {
                            this.mfMotorStatus = this.MotorStatus;
                            Task.Run(() => {
                                this.OnStatusChangedRaise();
                            });
                            Task.Delay(100).Wait();
                        }
                    }
                });
            }
        }

        ~AdLinkMotor() {
            this.Dispose(isFinalizer: true);
        }

        public void Dispose() {
            this.Dispose(isFinalizer: false);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放非托管资源;等待独立线程退出
        /// </summary>
        /// <param name="isFinalizer">是否为终结器(析构函数)回调</param>
        [MethodImpl(MethodImplOptions.Synchronized)]
        private void Dispose(bool isFinalizer) {
            this.mfIsPolling = false;
            if (isFinalizer) {
                if (!this.mfIsPolling) {
                    this.mfPollingTask?.Wait();
                }
            }
        }

        
    }
}
